// 文件名：graphics_vertex_buffer.cpp
// 目的：生成一个基于经纬参数化（latitude/longitude）的单位球体顶点缓冲（仅顶点，不含索引），
//      并打印生成的顶点数量与首个顶点的位置。
// 说明：
//   - 顶点包含 position（位置）、normal（法线）、uv（纹理坐标）。
//   - 该实现为“非索引顶点网格”（每个经纬格点输出一个顶点）；实际渲染中通常还需要
//     生成索引缓冲（index buffer）来组织三角形，减少顶点重复与带宽占用。
//   - 经纬参数化不可避免地在经度收尾处产生“接缝”（lon=0 与 lon=max 重合），以及极点（北/南极）汇聚；
//     为保证纹理正确展开，接缝处通常需要顶点重复与适当的 UV 处理。

#include <vector>
#include <iostream>
#include <cmath>

// 顶点格式：交错布局（Interleaved），便于直接上传给图形 API（OpenGL/Vulkan/DirectX）
// position：单位球上的 3D 坐标
// normal  ：单位法线（单位球上等同于 position）
// uv      ：[0,1] 范围的纹理坐标（u 对应经度，v 对应纬度）
struct Vertex {
    float position[3];
    float normal[3];
    float uv[2];
};

// 生成单位球顶点（非索引网格）
// 参数：
//   latitudeSegments  —— 纬度段数（沿 θ，从北极到南极，包含两极会多出 1 条“环”）
//   longitudeSegments —— 经度段数（沿 φ，绕 Y 轴一周，首尾重合会多出 1 列顶点以处理接缝）
// 返回：按 (latitudeSegments + 1) * (longitudeSegments + 1) 个顶点的数组
// 数学：
//   θ ∈ [0, π]  （latitude，0 为北极，π 为南极）
//   φ ∈ [0, 2π]（longitude，0 与 2π 重合，形成接缝）
//   单位球参数化：
//     x = sin(θ) * cos(φ)
//     y = cos(θ)
//     z = sin(θ) * sin(φ)
//   UV 映射：
//     u = lon / longitudeSegments ∈ [0,1]
//     v = lat / latitudeSegments  ∈ [0,1]
// 注意：
//   - 顶点数量使用 reserve 预留以避免多次扩容。
//   - 对于单位球，法线与位置相同（已归一化），若缩放为椭球则需重新归一化。
//   - 生成的顶点适合配合索引生成两个三角形组成的四边形面片（每个经纬网格单元）进行渲染。
std::vector<Vertex> generateSphere(int latitudeSegments, int longitudeSegments) {
    constexpr float PI = 3.14159265358979323846f; // 圆周率（float 精度）
    std::vector<Vertex> vertices;
    vertices.reserve((latitudeSegments + 1) * (longitudeSegments + 1)); // 预留容量，减少动态分配

    for (int lat = 0; lat <= latitudeSegments; ++lat) {
        // θ：从北极（0）到南极（π）
        float theta = static_cast<float>(lat) / latitudeSegments * PI;

        for (int lon = 0; lon <= longitudeSegments; ++lon) {
            // φ：从 0 到 2π，首尾重合形成接缝（保证 u=0 与 u=1 都有顶点，便于纹理采样）
            float phi = static_cast<float>(lon) / longitudeSegments * 2.0f * PI;

            // 单位球参数化坐标（注意 std::sin/std::cos 存在 float 重载，此处参数为 float）
            float x = std::sin(theta) * std::cos(phi);
            float y = std::cos(theta);
            float z = std::sin(theta) * std::sin(phi);

            Vertex v{}; // 值初始化，确保所有分量为 0（随后按需填充）
            // 位置
            v.position[0] = x; v.position[1] = y; v.position[2] = z;
            // 法线（单位球：法线即位置向量）
            v.normal[0] = x;   v.normal[1] = y;   v.normal[2] = z;
            // 纹理坐标（经度映射到 u，纬度映射到 v）
            // 注意：在极点附近多条经度线会收敛到一个点，u 值不同但位置相同，这在纹理展开是正常现象。
            v.uv[0] = static_cast<float>(lon) / longitudeSegments; // u ∈ [0,1]
            v.uv[1] = static_cast<float>(lat) / latitudeSegments;  // v ∈ [0,1]

            vertices.emplace_back(v); // 追加顶点
        }
    }
    return vertices;
}

int main() {
    // 示例：生成一个 8×16（纬×经）分段的球体网格
    // 顶点总数 = (8 + 1) * (16 + 1) = 153
    auto vertices = generateSphere(8, 16);

    // 输出顶点数量（仅顶点个数，不含索引/面数）
    std::cout << "Generated vertices: " << vertices.size() << '\n';

    // 打印第一个顶点的位置，用于基本验证
    if (!vertices.empty()) {
        const auto& v = vertices.front();
        std::cout << "First vertex position: (" << v.position[0] << ", "
                  << v.position[1] << ", " << v.position[2] << ")\n";
    }

    // 提示：
    // - 若要绘制三角形网格，请为每个经纬格单元（lat in [0, n-1], lon in [0, m-1]）
    //   生成 6 个索引（两个三角形），并注意经度收尾的索引连接。
    // - 如需无缝法线贴图，需进一步生成切线/副切线（tangent/bitangent）。
    // - 为避免极点附近的纹理拉伸，可采用不同的 UV 展开策略或多重纹理集。
    return 0;
}